/**
 * @UiWindow.cpp
 * Copyright 2013 (C) FatalEror / Dean Bahari
 * 
 * UiPack is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * See LICENSE file in main folder for more information.
 */

#include "UiCommon.hpp"
#include "UiApp.hpp"
#include "UiBase.hpp"
#include "UiButton.hpp"
#include "UiControl.hpp"
#include "UiEvent.hpp"
#include "UiMenubar.hpp"
#include "UiResource.hpp"
#include "UiSizer.hpp"
#include "UiWindow.hpp"

UiBaseWindow::UiBaseWindow(const wchar_t *title, int width, int height, int x, int y)
	:UiBase(title)
{	
	SetWindowLong(this->getHandle(), GWL_STYLE, GetWindowLong(this->getHandle(), GWL_STYLE) & (~WS_OVERLAPPEDWINDOW));

	this->setSize(width, height);
	if (x > 0 && y > 0)
		this->setPosition(x, y);
	this->setTitle(title);
	mState           = 0;
	mSysState        = 0;
}

UiBaseWindow::~UiBaseWindow()
{
	MessageBox(NULL, L"DELTE", L"DELETE", MB_OK);
}

void UiBaseWindow::setTitle(const wchar_t *title)
{
	mTitle = title;
}

const wchar_t *UiBaseWindow::getTitle()
{
	return mTitle.c_str();
}

int UiBaseWindow::getState()
{
	return mState;
}

void UiBaseWindow::show()
{
	ShowWindow(this->getHandle(), SW_SHOWDEFAULT);
	UpdateWindow(this->getHandle());
}

void UiBaseWindow::onMinimize(UiEvent &ev)
{
	SetWindowLong(this->getHandle(), GWL_STYLE, GetWindowLong(this->getHandle(), GWL_STYLE) | WS_CAPTION);
	ShowWindow(this->getHandle(), SW_MINIMIZE);
}

void UiBaseWindow::onRestore(UiEvent &ev)
{
	ShowWindow(this->getHandle(), SW_RESTORE);
	SetWindowLong(this->getHandle(), GWL_STYLE, GetWindowLong(this->getHandle(), GWL_STYLE) & ~WS_CAPTION);
}

void UiBaseWindow::onResize(UiEvent &ev)
{
	ShowWindow(this->getHandle(), ev.getState() & UiBaseWindow::UI_WINDOW_MAXIMIZED ? SW_MAXIMIZE : SW_RESTORE);
}

void UiBaseWindow::onClose(UiEvent &ev)
{
	DestroyWindow(this->getHandle());
}

LRESULT CALLBACK UiBaseWindow::handleCustomProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	return DefWindowProc(this->getHandle(), msg, wParam, lParam);
}

UiWindow::UiWindow(const wchar_t *title, int width, int height, int x, int y)
	:UiBaseWindow(title, width, height, x, y), mMaxSize(NULL), mMinSize(NULL), mMenubar(NULL)
{
	SetWindowLong(this->getHandle(), GWL_EXSTYLE, GetWindowLong(this->getHandle(), GWL_EXSTYLE) | WS_EX_LAYERED);
	SetLayeredWindowAttributes(this->getHandle(), 0, 0, LWA_COLORKEY);

	if (UiApp::getOSVersion() >= UiApp::UI_OS_WINVISTA) {
		SetWindowLong(this->getHandle(), GWL_STYLE, GetWindowLong(this->getHandle(), GWL_STYLE) | (WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX));
		int ncrp = 1;
		UiApp::DwmSetWindowAttribute(this->getHandle(), 2, &ncrp, sizeof(ncrp));
	} else
		SetWindowLong(this->getHandle(), GWL_STYLE, GetWindowLong(this->getHandle(), GWL_STYLE) | (WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_CAPTION));
}

UiWindow::~UiWindow()
{
	if (mMinSize)
		delete mMinSize;
	if (mMaxSize)
		delete mMaxSize;
}

void UiWindow::setMinSize(short width, short height)
{
	if (!mMinSize)
		mMinSize = new POINT;
	mMinSize->x = width;
	mMinSize->y = height;
}

void UiWindow::setMaxSize(short width, short height)
{
	if (!mMaxSize)
		mMaxSize = new POINT;
	mMaxSize->x = width;
	mMaxSize->y = height;
}

void UiWindow::setMenubar(UiMenubar *menubar)
{
	mMenubar = menubar;
	this->addChild(menubar);
	menubar->setPosition(0, 0);
	menubar->setSize(this->getWidth(), 32);
	ShowWindow(menubar->getHandle(), SW_SHOWDEFAULT);
	if (getSizer())
		getSizer()->setPosition(0, 32);
}

void UiWindow::setSizer(UiSizer *sizer)
{
	UiBase::setSizer(sizer);
	if (mMenubar)
		sizer->setPosition(0, 32);
}

LRESULT CALLBACK UiWindow::handleCustomProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_KEYDOWN:
		if (wParam == VK_BACK) {
			SetWindowPos(this->getHandle(), NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
		}
		break;
	case WM_GETMINMAXINFO:
	{
		LPMINMAXINFO mmi = reinterpret_cast<LPMINMAXINFO>(lParam);
		if (mMinSize != NULL)
			mmi->ptMinTrackSize = *mMinSize;
		if (mMaxSize != NULL)
			mmi->ptMaxTrackSize = *mMaxSize;
	} break;
	case WM_SYSCOMMAND:
	{
		switch (wParam) {
		case SC_MINIMIZE:
		{
			UiEvent evMinimize;
			this->onMinimize(evMinimize);
		} break;
		case SC_RESTORE:
		{
			UiEvent evRestore;
			this->onRestore(evRestore);
		} break;
		default:
			return DefWindowProc(this->getHandle(), msg, wParam, lParam);
		}
	}
	case WM_MOUSELEAVE:
	{
		if (mSysState != 0) {
			mSysState = 0;
			HDC hDC = GetWindowDC(this->getHandle());
			HDC hDCBuffer = UiApp::getCurrentApp()->getDCBuffer();

			Gdiplus::Graphics grBuffer(hDCBuffer);
			grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_NORMAL), Gdiplus::Rect(0, 0, 83, 18), 0, 0, 83, 18, Gdiplus::UnitPixel);

			BitBlt(hDC, this->getWidth() - 90, 0, 83, 18, hDCBuffer, 0, 0, SRCCOPY);
			ReleaseDC(this->getHandle(), hDC);
		}
	} return DefWindowProc(this->getHandle(), msg, wParam, lParam);
	case WM_NCHITTEST:
	{
		RECT rc;
		GetWindowRect(this->getHandle(), &rc);
		POINT pt  = { LOWORD(lParam) - rc.left, HIWORD(lParam) - rc.top };

		bool needRedraw = false;
		int  retVal     = HTCLIENT;
		
		if (pt.y < UI_WINDOWSIZE_TITLE) {
			if (pt.x > this->getWidth() - 90 && pt.x < this->getWidth() - 7) {
				if (pt.y < 3)
					retVal = HTTOP;
				else if (pt.y >= 20) {
					retVal = HTCAPTION;
					if (mSysState) {
						mSysState  = 0;
						needRedraw = true;
					}
				} else {
					TRACKMOUSEEVENT tme;
					tme.cbSize = sizeof(tme);
					tme.dwFlags = TME_LEAVE;
					tme.dwHoverTime = INFINITE;
					tme.hwndTrack = this->getHandle();
					TrackMouseEvent(&tme);

					if (pt.x < this->getWidth() - 64) {
						if (!(mSysState & UiBaseWindow::UI_SYS_MINIMIZE)) {
							mSysState  = UiBaseWindow::UI_SYS_MINIMIZE | UiBaseWindow::UI_SYS_HOVER;
							needRedraw = true;
						}
					} else if (pt.x < this->getWidth() - 36) {
						if (!(mSysState & UiBaseWindow::UI_SYS_RESIZE)) {
							mSysState  = UiBaseWindow::UI_SYS_RESIZE | UiBaseWindow::UI_SYS_HOVER;
							needRedraw = true;
						}
					} else {
						if (!(mSysState & UiBaseWindow::UI_SYS_CLOSE)) {
							mSysState  = UiBaseWindow::UI_SYS_CLOSE | UiBaseWindow::UI_SYS_HOVER;
							needRedraw = true;
						}
					}
				}
			} else {
				if (mSysState != 0) {
					mSysState  = 0;
					needRedraw = true;
				}
				if (pt.x < UI_WINDOWSIZE_BORDER)
					retVal = HTTOPLEFT;
				else if (pt.x > this->getWidth() - UI_WINDOWSIZE_BORDER)
					retVal = HTTOPRIGHT;
				else if (pt.y < UI_WINDOWSIZE_BORDER)
					retVal = HTTOP;
				else
					retVal = HTCAPTION;
			}
		} else {
			if (mSysState != 0) {
				mSysState  = 0;
				needRedraw = true;
			}
			if (pt.y > this->getHeight() - UI_WINDOWSIZE_BORDER) {
				if (pt.x < UI_WINDOWSIZE_BORDER)
					retVal = HTBOTTOMLEFT;
				else if (pt.x > this->getWidth() - UI_WINDOWSIZE_BORDER)
					retVal = HTBOTTOMRIGHT;
				else
					retVal = HTBOTTOM;
			} else {
				if (pt.x < UI_WINDOWSIZE_BORDER)
					retVal = HTLEFT;
				else if (pt.x > this->getWidth() - UI_WINDOWSIZE_BORDER)
					retVal = HTRIGHT;
			}
		}

		if (needRedraw) {
			HDC hDC = GetWindowDC(this->getHandle());
			HDC hDCBuffer = UiApp::getCurrentApp()->getDCBuffer();

			Gdiplus::Graphics grBuffer(hDCBuffer);
			grBuffer.FillRectangle(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_BLANK), 0, 0, 83, 18);
			grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_NORMAL), Gdiplus::Rect(0, 0, 83, 18), 0, 0, 83, 18, Gdiplus::UnitPixel);

			if (mSysState) {
				if (mSysState & UiBaseWindow::UI_SYS_MINIMIZE)
					grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_HOVER), Gdiplus::Rect(0, 0, 26, 18), 0, 0, 26, 18, Gdiplus::UnitPixel);
				else if (mSysState & UiBaseWindow::UI_SYS_RESIZE)
					grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_HOVER), Gdiplus::Rect(27, 0, 27, 18), 27, 0, 27, 18, Gdiplus::UnitPixel);
				else {
					grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_HOVER), Gdiplus::Rect(55, 0, 27, 18), 55, 0, 27, 18, Gdiplus::UnitPixel);
				}
			}
			BitBlt(hDC, this->getWidth() - 90, 0, 83, 18, hDCBuffer, 0, 0, SRCCOPY);
			ReleaseDC(this->getHandle(), hDC);
		}

		return ((this->getState() & UiBaseWindow::UI_WINDOW_MAXIMIZED) && retVal >= 10 ? 0 : retVal);
	} break;
	case WM_LBUTTONDOWN:
	{
		if (mSysState) {
			SetCapture(this->getHandle());

			mSysState &= ~UiBaseWindow::UI_SYS_HOVER;
			mSysState |= UiBaseWindow::UI_SYS_CLICK;

			HDC hDC = GetWindowDC(this->getHandle());
			HDC hDCBuffer = UiApp::getCurrentApp()->getDCBuffer();

			Gdiplus::Graphics grBuffer(hDCBuffer);
			if (mSysState & UiBaseWindow::UI_SYS_MINIMIZE)
				grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_CLICK), Gdiplus::Rect(0, 0, 26, 20), 0, 0, 26, 20, Gdiplus::UnitPixel);
			else if (mSysState & UiBaseWindow::UI_SYS_RESIZE)
				grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_CLICK), Gdiplus::Rect(27, 0, 27, 20), 27, 0, 27, 20, Gdiplus::UnitPixel);
			else
				grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_CLICK), Gdiplus::Rect(55, 0, 27, 20), 55, 0, 27, 20, Gdiplus::UnitPixel);
			BitBlt(hDC, this->getWidth() - 90, 0, 83, 18, hDCBuffer, 0, 0, SRCCOPY);
			ReleaseDC(this->getHandle(), hDC);
		} else
			return DefWindowProc(this->getHandle(), msg, wParam, lParam);
	} break;
	case WM_LBUTTONUP:
	{
		if (mSysState) {
			ReleaseCapture();

			HDC hDC   = GetWindowDC(this->getHandle());
			HDC hDCBuffer = UiApp::getCurrentApp()->getDCBuffer();

			Gdiplus::Graphics grBuffer(hDCBuffer);
			grBuffer.FillRectangle(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_BLANK), 0, 0, 83, 18);
			grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_NORMAL), Gdiplus::Rect(0, 0, 83, 18), 0, 0, 83, 18, Gdiplus::UnitPixel);

			BitBlt(hDC, this->getWidth() - 90, 0, 83, 18, hDCBuffer, 0, 0, SRCCOPY);
			ReleaseDC(this->getHandle(), hDC);

			POINT pt = { static_cast<short>(LOWORD(lParam) + UI_WINDOWSIZE_BORDER), static_cast<short>(HIWORD(lParam) + UI_WINDOWSIZE_TITLE) };

			if (pt.y < UI_WINDOWSIZE_TITLE && pt.y > 0) {
				if (pt.x > this->getWidth() - 90 && pt.x < this->getWidth() - 7) {
					if (pt.x < this->getWidth() - 64) {
						if (mSysState & UiBaseWindow::UI_SYS_MINIMIZE) {
							UiEvent evMinimize;
							this->onMinimize(evMinimize);
						}
					} else if (pt.x < this->getWidth() - 36) {
						if (mSysState & UiBaseWindow::UI_SYS_RESIZE) {
							UiEvent evResize((mState & UiBaseWindow::UI_WINDOW_MAXIMIZED) ? UiEvent::UI_WINDOW_RESTORED : UiBaseWindow::UI_WINDOW_MAXIMIZED);
							this->onResize(evResize);
						}
					} else {
						if (mSysState & UiBaseWindow::UI_SYS_CLOSE) {
							UiEvent evClose;
							this->onClose(evClose);
						}
					}
				}
			}
			mSysState = 0;
		}
	} break;
	case WM_NCACTIVATE:
	case WM_ERASEBKGND:
	{
	} return 1;
	case WM_NCPAINT:
	{
		if (UiApp::getOSVersion() < UiApp::UI_OS_WINVISTA)
			SetWindowRgn(this->getHandle(), NULL, false);

		HDC hDC = GetWindowDC(this->getHandle());
		HDC hDCBuffer = UiApp::getCurrentApp()->getDCBuffer();

		RECT rc;
		GetWindowRect(this->getHandle(), &rc);
		mWidth  = rc.right - rc.left;
		mHeight = rc.bottom - rc.top;

		ExcludeClipRect(hDC, UI_WINDOWSIZE_BORDER, UI_WINDOWSIZE_TITLE, this->getWidth() - UI_WINDOWSIZE_BORDER, this->getHeight() - UI_WINDOWSIZE_BORDER);

		Gdiplus::Graphics grBuffer(hDCBuffer);

		grBuffer.FillRectangle(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_BORDER), 1, UI_WINDOWSIZE_TITLE - 1, UI_WINDOWSIZE_BORDER - 1, this->getHeight() - UI_WINDOWSIZE_TITLE - 1);
		grBuffer.FillRectangle(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_BORDER), 1, UI_WINDOWSIZE_TITLE - 1, UI_WINDOWSIZE_BORDER - 1, this->getHeight() - UI_WINDOWSIZE_TITLE - 1);
		grBuffer.FillRectangle(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_BORDER), this->getWidth() - UI_WINDOWSIZE_BORDER, UI_WINDOWSIZE_TITLE - 1, UI_WINDOWSIZE_BORDER - 1, this->getHeight() - UI_WINDOWSIZE_TITLE - 1);
		grBuffer.FillRectangle(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_BORDER), 1, this->getHeight() - UI_WINDOWSIZE_BORDER, this->getWidth() - 2, UI_WINDOWSIZE_BORDER - 1);
		grBuffer.DrawRectangle(UiApp::getResource()->getPen(UiResource::UI_PEN_BORDER), 0, 5, this->getWidth() - 1, this->getHeight() - 6);
		grBuffer.FillRectangle(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_BLANK), 0, 0, this->getWidth(), UI_WINDOWSIZE_TITLE);
		grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_TITLEBAR), Gdiplus::Rect(0, 0, 5, 25), 0, 0, 5, 25, Gdiplus::UnitPixel);
		grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_TITLEBAR), Gdiplus::Rect(5, 0, this->getWidth() - 10, 25), 5, 0, 15, 25, Gdiplus::UnitPixel);
		grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_TITLEBAR), Gdiplus::Rect(this->getWidth() - 5, 0, 5, 25), 20, 0, 5, 25, Gdiplus::UnitPixel);
		grBuffer.DrawRectangle(UiApp::getResource()->getPen(UiResource::UI_PEN_BORDER), UI_WINDOWSIZE_BORDER - 1, UI_WINDOWSIZE_TITLE + (mMenubar ? 31 : -1), this->getWidth() - UI_WINDOWSIZE_BORDER * 2 + 1, this->getHeight() - UI_WINDOWSIZE_TITLE - UI_WINDOWSIZE_BORDER - (mMenubar ? 31 : -1));

		Gdiplus::GraphicsPath path;
		path.AddString(this->getTitle(), -1, UiApp::getResource()->getFont(), Gdiplus::FontStyleRegular, 12, Gdiplus::PointF(8.0f, 6.0f), NULL);
		grBuffer.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
		grBuffer.DrawPath(UiApp::getResource()->getPen(UiResource::UI_PEN_TEXT), &path);
		grBuffer.FillPath(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_TEXT), &path);
		grBuffer.SetSmoothingMode(Gdiplus::SmoothingModeNone);
		grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_SYS_NORMAL), Gdiplus::Rect(this->getWidth() - 90, 0, 83, 20), 0, 0, 83, 20, Gdiplus::UnitPixel);

		BitBlt(hDC, 0, 0, this->getWidth(), this->getHeight(), hDCBuffer, 0, 0, SRCCOPY);
		ReleaseDC(this->getHandle(), hDC);

	} break;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hDC = BeginPaint(this->getHandle(), &ps);

		Gdiplus::Graphics grBuffer(hDC);
		grBuffer.FillRectangle(UiApp::getResource()->getBrush(this->getBkColor()), 0, 0, this->getWidth(), this->getHeight());

		EndPaint(this->getHandle(), &ps);
	} break;
	case WM_NCCALCSIZE:
	{
		LPNCCALCSIZE_PARAMS csp = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);

		csp->rgrc[0].top    += 25;
		csp->rgrc[0].left   += 8;
		csp->rgrc[0].right  -= 8;
		csp->rgrc[0].bottom -= 8;
		
		if (mMenubar)
			mMenubar->setSize(csp->rgrc[0].right - csp->rgrc[0].left, 32);
		if (this->getSizer())
			this->getSizer()->handleResize(csp->rgrc[0].right - csp->rgrc[0].left, csp->rgrc[0].bottom - csp->rgrc[0].top - (mMenubar ? 32 : 0));
		RedrawWindow(this->getHandle(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
	} break;
	case WM_SIZE:
	{
		switch (wParam) {
		case SIZE_MAXIMIZED:
			mState = UiWindow::UI_WINDOW_MAXIMIZED;
			break;
		case SIZE_MINIMIZED:
			mState = UiWindow::UI_WINDOW_MINIMIZED;
			break;
		default:
			mState = 0;
			break;
		}
	} break;
	default:
		return DefWindowProc(this->getHandle(), msg, wParam, lParam);
	}

	return 0;
}

UiToolWindow::UiToolWindow(const wchar_t *title, int width, int height, int x, int y)
	:UiBaseWindow(title, width, height, x, y)
{
	mAttached = false;
	mFocused  = false;
	mBkColor  = UiResource::UI_BRUSH_TOOLWINDOW;
}

void UiToolWindow::setOwner(UiWindow *owner)
{
	SetWindowLong(this->getHandle(), GWL_HWNDPARENT, reinterpret_cast<LONG>(owner->getHandle()));
}

void UiToolWindow::mainFocus(bool isFocus)
{
	if (isFocus) {
		if (!mFocused) {
			EnableWindow((HWND)GetWindowLong(this->getHandle(), GWL_HWNDPARENT), false);
			mFocused = true;
		}
	} else {
		if (mFocused) {
			EnableWindow((HWND)GetWindowLong(this->getHandle(), GWL_HWNDPARENT), true);
			mFocused = false;
		}
	}
}

LRESULT CALLBACK UiToolWindow::handleCustomProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_DESTROY:
		if (mFocused) {
			EnableWindow((HWND)GetWindowLong(this->getHandle(), GWL_HWNDPARENT), true);
			SetActiveWindow((HWND)GetWindowLong(this->getHandle(), GWL_HWNDPARENT));
		}
		break;
	case WM_MOUSELEAVE:
	{
		if (mSysState != 0) {
			mSysState  = 0;
			HDC hDC = GetWindowDC(this->getHandle());
			Gdiplus::Graphics grBuffer(hDC);
			grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_TOOL_CLOSE), Gdiplus::Rect(this->getWidth() - 17, 3, 14, 15), 0, 0, 14, 15, Gdiplus::UnitPixel);
			ReleaseDC(this->getHandle(), hDC);
		}
	} return DefWindowProc(this->getHandle(), msg, wParam, lParam);
	case WM_NCACTIVATE:
	case WM_ERASEBKGND:
		return 1;
	case WM_NCHITTEST:
	{
		bool needRedraw = false;
		int  retVal     = HTCLIENT;

		RECT rc;
		GetWindowRect(this->getHandle(), &rc);
		POINT pt = { LOWORD(lParam) - rc.left, HIWORD(lParam) - rc.top };

		if (pt.y < 20 && !mAttached) {
			if (pt.x > this->getWidth() - 16 && pt.x < this->getWidth() - 2) {
				mSysState  = UiBaseWindow::UI_SYS_CLOSE | UiBaseWindow::UI_SYS_HOVER;
				needRedraw = true;

				TRACKMOUSEEVENT tme;
				tme.cbSize = sizeof(tme);
				tme.dwFlags = TME_LEAVE;
				tme.dwHoverTime = INFINITE;
				tme.hwndTrack = this->getHandle();
				TrackMouseEvent(&tme);
			} else {
				if (mSysState != 0) {
					mSysState  = 0;
					needRedraw = true;
				}
				retVal = HTCAPTION;
			}
		} else {
			if (mSysState != 0) {
				mSysState  = 0;
				needRedraw = true;
			}
		}
		if (needRedraw) {
			HDC hDC = GetWindowDC(this->getHandle());
			Gdiplus::Graphics grBuffer(hDC);
			if (mSysState == 0)
				grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_TOOL_CLOSE), Gdiplus::Rect(this->getWidth() - 17, 3, 14, 15), 0, 0, 14, 15, Gdiplus::UnitPixel);
			else if (mSysState & UiBaseWindow::UI_SYS_CLOSE)
				grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_TOOL_CLOSE), Gdiplus::Rect(this->getWidth() - 17, 3, 14, 15), 14, 0, 14, 15, Gdiplus::UnitPixel);
			ReleaseDC(this->getHandle(), hDC);
		}

		return retVal;
	}
	case WM_LBUTTONDOWN:
	{
		if (mSysState) {
			SetCapture(this->getHandle());

			mSysState &= ~UiBaseWindow::UI_SYS_HOVER;
			mSysState |= UiBaseWindow::UI_SYS_CLICK;

			HDC hDC = GetWindowDC(this->getHandle());

			Gdiplus::Graphics grBuffer(hDC);
			grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_TOOL_CLOSE), Gdiplus::Rect(this->getWidth() - 17, 3, 14, 15), 28, 0, 14, 15, Gdiplus::UnitPixel);
			ReleaseDC(this->getHandle(), hDC);
		} else
			return DefWindowProc(this->getHandle(), msg, wParam, lParam);
	} break;
	case WM_LBUTTONUP:
	{
		if (mSysState) {
			ReleaseCapture();

			HDC hDC   = GetWindowDC(this->getHandle());
			Gdiplus::Graphics grBuffer(hDC);
			grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_TOOL_CLOSE), Gdiplus::Rect(this->getWidth() - 17, 3, 14, 15), 0, 0, 14, 15, Gdiplus::UnitPixel);
			ReleaseDC(this->getHandle(), hDC);

			POINT pt = { static_cast<short>(LOWORD(lParam) + 1), static_cast<short>(HIWORD(lParam) + 20) };

			if (pt.y > 0 && pt.y < 20 && !mAttached) {
				if (pt.x > this->getWidth() - 16 && pt.x < this->getWidth() - 2) {
					UiEvent evClose;
					this->onClose(evClose);
				}
			}
			mSysState = 0;
		}
	} break;
	case WM_NCPAINT:
	{
		if (UiApp::getOSVersion() < UiApp::UI_OS_WINVISTA)
			SetWindowRgn(this->getHandle(), NULL, false);

		HDC hDC = GetWindowDC(this->getHandle());
		HDC hDCBuffer = UiApp::getCurrentApp()->getDCBuffer();

		RECT rc;
		GetWindowRect(this->getHandle(), &rc);
		mWidth  = rc.right - rc.left;
		mHeight = rc.bottom - rc.top;

		ExcludeClipRect(hDC, 1, 20, this->getWidth() - 1, this->getHeight() - 1);

		Gdiplus::Graphics grBuffer(hDCBuffer);
		grBuffer.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHalf);
		grBuffer.SetInterpolationMode(Gdiplus::InterpolationModeNearestNeighbor);
		grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_TOOL_TITLE), Gdiplus::Rect(0, 0, this->getWidth(), 19), 0, 0, 1, 19, Gdiplus::UnitPixel);

		grBuffer.SetPixelOffsetMode(Gdiplus::PixelOffsetModeDefault);
		grBuffer.DrawRectangle(UiApp::getResource()->getPen(UiResource::UI_PEN_BORDER), 0, 0, this->getWidth() - 1, this->getHeight() - 1);
		grBuffer.DrawLine(UiApp::getResource()->getPen(UiResource::UI_PEN_BORDER), 0, 19, this->getWidth() - 1, 19);
		if (!mAttached)
			grBuffer.DrawImage(UiApp::getResource()->getImage(UiResource::UI_IMAGE_TOOL_CLOSE), Gdiplus::Rect(this->getWidth() - 17, 3, 14, 15), 0, 0, 14, 15, Gdiplus::UnitPixel);

		Gdiplus::GraphicsPath path;
		path.AddString(this->getTitle(), -1, UiApp::getResource()->getFont(), Gdiplus::FontStyleRegular, 11, Gdiplus::PointF(2.0f, 2.0f), NULL);
		grBuffer.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
		grBuffer.DrawPath(UiApp::getResource()->getPen(UiResource::UI_PEN_TEXT), &path);
		grBuffer.FillPath(UiApp::getResource()->getBrush(UiResource::UI_BRUSH_TEXT), &path);
		grBuffer.SetSmoothingMode(Gdiplus::SmoothingModeNone);

		BitBlt(hDC, 0, 0, this->getWidth(), this->getHeight(), hDCBuffer, 0, 0, SRCCOPY);
		ReleaseDC(this->getHandle(), hDC);
	} break;
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hDC = BeginPaint(this->getHandle(), &ps);

		Gdiplus::Graphics grBuffer(hDC);
		grBuffer.FillRectangle(UiApp::getResource()->getBrush(this->getBkColor()), 0, 0, this->getWidth(), this->getHeight());

		EndPaint(this->getHandle(), &ps);
	} break;
	case WM_NCCALCSIZE:
	{
		LPNCCALCSIZE_PARAMS csp = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
		csp->rgrc[0].top    += 20;
		csp->rgrc[0].left   += 1;
		csp->rgrc[0].right  -= 1;
		csp->rgrc[0].bottom -= 1;

		if (this->getSizer())
			this->getSizer()->handleResize(csp->rgrc[0].right - csp->rgrc[0].left, csp->rgrc[0].bottom - csp->rgrc[0].top);
		RedrawWindow(this->getHandle(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
	} break;
	case WM_SIZE:
	{
	} break;
	default:
		return DefWindowProc(this->getHandle(), msg, wParam, lParam);
	}
	return 0;
}